package com.demo.config.watermark;

import cn.hutool.core.util.StrUtil;
import com.itextpdf.text.DocumentException;
import com.itextpdf.text.Element;
import com.itextpdf.text.pdf.*;
import com.microsoft.schemas.office.office.CTLock;
import com.microsoft.schemas.vml.*;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.wp.usermodel.HeaderFooterType;
import org.apache.poi.xssf.usermodel.XSSFRelation;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.apache.poi.xwpf.usermodel.XWPFDocument;
import org.apache.poi.xwpf.usermodel.XWPFHeader;
import org.openxmlformats.schemas.officeDocument.x2006.sharedTypes.STTrueFalse;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.*;
import org.springframework.mock.web.MockMultipartFile;
import org.springframework.web.multipart.MultipartFile;

import javax.imageio.ImageIO;
import javax.swing.*;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.*;
import java.util.stream.Stream;

public class WaterMarkUtilsNEW {

	// word字体
	private static final String fontName = "宋体";

	// 字体大小
	private static final String fontSize = "0.2pt";

	// 字体颜色
	private static final String fontColor = "#d0d0d0";

	// 一个字平均长度，单位pt，用于：计算文本占用的长度（文本总个数*单字长度）
	private static final Integer widthPerWord = 10;

	// 与顶部的间距
	private static Integer styleTop = 0;

	// 文本旋转角度
	private static final String styleRotation = "45";

	public static void main(String[] args) throws IOException {
		File file = new File("I:/Desktop/图片/word/1111.XLSX");
		// word
		// word(file,"王葛瑞");
		setExcelWaterMark(file, "王葛瑞", "xlsx");
	}

	// https://blog.csdn.net/huoxing0303/article/details/128275267?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522169234623616800185884704%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fall.%2522%257D&request_id=169234623616800185884704&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~first_rank_ecpm_v1~rank_v31_ecpm-2-128275267-null-null.142^v93^insert_down1&utm_term=XWPFDocument%20doc%20%E5%8A%A0%E6%B0%B4%E5%8D%B0%E5%9B%BE%E7%89%87java&spm=1018.2226.3001.4187
	private static void word(File file, String markText) throws IOException {
		XWPFDocument doc = new XWPFDocument(new FileInputStream(file));
		waterMarkDocXDocument(doc, markText);
		// 保存位置
		String downloadPath = "I:/Desktop/图片/word/" + System.currentTimeMillis() + "_"
				+ StrUtil.subAfter("I:/Desktop/图片/word/123.DOCX", "/", true);

		// 生成输出文件
		File file11 = new File(downloadPath);
		file11.createNewFile();
		doc.write(new FileOutputStream(file11));
		doc.close();

	}

	/**
	 * @param file:
	 * @param markText:
	 * @param type:
	 * @return: org.springframework.web.multipart.MultipartFile
	 * @description: word文档类型判断后添加水印
	 */
	public static MultipartFile setWordWaterMark(MultipartFile file, String markText, String type) {
		if (type.equals("docx")) {
			try {
				XWPFDocument doc = new XWPFDocument(file.getInputStream());
				waterMarkDocXDocument(doc, markText);
				ByteArrayOutputStream baos = new ByteArrayOutputStream();// 二进制OutputStream
				doc.write(baos);// 文档写入流
				ByteArrayInputStream in = new ByteArrayInputStream(baos.toByteArray());// OutputStream写入InputStream二进制流
				MultipartFile multipartFile = new MockMultipartFile(file.getName(), file.getOriginalFilename(), "", in);
				return multipartFile;
			}
			catch (FileNotFoundException e) {
				e.printStackTrace();
			}
			catch (IOException e) {
				e.printStackTrace();
			}
		}
		else if (type.equals("xlsx") || type.equals("xls")) {
			return null;
		}
		else if (type.equals("pdf")) {
			return setPDFWatermark(file, markText);
		}
		return file;
	}

	/**********************************
	 * ---------------------docx添加水印----------------------
	 **********************************/

	/**
	 * 给文档添加水印 此方法可以单独使用
	 * @param doc
	 * @param customText
	 */
	public static void waterMarkDocXDocument(XWPFDocument doc, String customText) {
		// 把整页都打上水印
		for (int lineIndex = -5; lineIndex < 20; lineIndex++) {
			styleTop = 100 * lineIndex;
			waterMarkDocXDocument_0(doc, customText);
		}
	}

	/**
	 * 为文档添加水印
	 * @param doc 需要被处理的docx文档对象
	 * @param customText 需要添加的水印文字
	 */
	public static void waterMarkDocXDocument_0(XWPFDocument doc, String customText) {
		// 水印文字之间使用8个空格分隔
		customText = customText + repeatString(" ", 8);
		// 一行水印重复水印文字次数
		customText = repeatString(customText, 10);
		// 如果之前已经创建过 DEFAULT 的Header，将会复用
		XWPFHeader header = doc.createHeader(HeaderFooterType.DEFAULT);
		int size = header.getParagraphs().size();
		if (size == 0) {
			header.createParagraph();
		}
		CTP ctp = header.getParagraphArray(0).getCTP();
		byte[] rsidr = doc.getDocument().getBody().getPArray(0).getRsidR();
		byte[] rsidrdefault = doc.getDocument().getBody().getPArray(0).getRsidRDefault();
		ctp.setRsidP(rsidr);
		ctp.setRsidRDefault(rsidrdefault);
		CTPPr ppr = ctp.addNewPPr();
		ppr.addNewPStyle().setVal("Header");
		// 开始加水印
		CTR ctr = ctp.addNewR();
		CTRPr ctrpr = ctr.addNewRPr();
		ctrpr.addNewNoProof();
		CTGroup group = CTGroup.Factory.newInstance();
		CTShapetype shapetype = group.addNewShapetype();
		CTTextPath shapeTypeTextPath = shapetype.addNewTextpath();
		shapeTypeTextPath.setOn(STTrueFalse.T);
		shapeTypeTextPath.setFitshape(STTrueFalse.T);
		CTLock lock = shapetype.addNewLock();
		lock.setExt(STExt.VIEW);
		CTShape shape = group.addNewShape();
		shape.setId("PowerPlusWaterMarkObject");
		shape.setSpid("_x0000_s102");
		shape.setType("#_x0000_t136");
		// 设置形状样式（旋转，位置，相对路径等参数）
		shape.setStyle(getShapeStyle(customText));
		shape.setFillcolor(fontColor);
		// 字体设置为实心
		shape.setStroked(STTrueFalse.FALSE);
		// 绘制文本的路径
		CTTextPath shapeTextPath = shape.addNewTextpath();
		// 设置文本字体与大小
		shapeTextPath.setStyle("font-family:" + fontName + ";font-size:" + fontSize);
		shapeTextPath.setString(customText);
		CTPicture pict = ctr.addNewPict();
		pict.set(group);
	}

	/**
	 * 构建Shape的样式参数
	 * @param customText
	 * @return
	 */
	private static String getShapeStyle(String customText) {
		StringBuilder sb = new StringBuilder();
		// 文本path绘制的定位方式
		sb.append("position: ").append("absolute");
		// 计算文本占用的长度（文本总个数*单字长度）
		sb.append(";width: ").append(customText.length() * widthPerWord).append("pt");
		// 字体高度
		sb.append(";height: ").append("20pt");
		sb.append(";z-index: ").append("-251654144");
		sb.append(";mso-wrap-edited: ").append("f");
		// 设置水印的间隔，这是一个大坑，不能用top,必须要margin-top。
		sb.append(";margin-top: ").append(styleTop);
		sb.append(";mso-position-horizontal-relative: ").append("page");
		sb.append(";mso-position-vertical-relative: ").append("page");
		sb.append(";mso-position-vertical: ").append("left");
		sb.append(";mso-position-horizontal: ").append("center");
		sb.append(";rotation: ").append(styleRotation);
		return sb.toString();
	}

	/**
	 * 将指定的字符串重复repeats次.
	 */
	private static String repeatString(String pattern, int repeats) {
		StringBuilder buffer = new StringBuilder(pattern.length() * repeats);
		Stream.generate(() -> pattern).limit(repeats).forEach(buffer::append);
		return new String(buffer);
	}

	/**
	 * MultipartFile 转 File
	 * @param file
	 * @throws Exception
	 */
	public static File multipartFileToFile(MultipartFile file) throws Exception {

		File toFile = null;
		if (file.equals("") || file.getSize() <= 0) {
			file = null;
		}
		else {
			InputStream ins = null;
			ins = file.getInputStream();
			toFile = new File(file.getOriginalFilename());
			inputStreamToFile(ins, toFile);
			ins.close();
		}
		return toFile;
	}

	// 获取流文件
	private static void inputStreamToFile(InputStream ins, File file) {
		try {
			OutputStream os = new FileOutputStream(file);
			int bytesRead = 0;
			byte[] buffer = new byte[8192];
			while ((bytesRead = ins.read(buffer, 0, 8192)) != -1) {
				os.write(buffer, 0, bytesRead);
			}
			os.close();
			ins.close();
		}
		catch (Exception e) {
			e.printStackTrace();
		}
	}

	/**********************************
	 * ---------------------Excel添加水印----------------------
	 **********************************/

	/**
	 * 将BufferedImage转换为InputStream
	 * @param image
	 * @return
	 */
	public static InputStream bufferedImageToInputStream(BufferedImage image) {
		ByteArrayOutputStream os = new ByteArrayOutputStream();
		try {
			ImageIO.write(image, "png", os);
			InputStream input = new ByteArrayInputStream(os.toByteArray());
			return input;
		}
		catch (IOException e) {
			e.printStackTrace();
		}
		return null;
	}

	/**
	 * 为Excel打上水印工具函数 请自行确保参数值，以保证水印图片之间不会覆盖。 在计算水印的位置的时候，并没有考虑到单元格合并的情况，请注意
	 * @param wb Excel Workbook
	 * @param sheet 需要打水印的Excel
	 * @param bufferImg 水印地址，classPath，目前只支持png格式的图片，
	 * 因为非png格式的图片打到Excel上后可能会有图片变红的问题，且不容易做出透明效果。
	 * 同时请注意传入的地址格式，应该为类似："\\excelTemplate\\test.png"
	 * @param startXCol 水印起始列
	 * @param startYRow 水印起始行
	 * @param betweenXCol 水印横向之间间隔多少列
	 * @param betweenYRow 水印纵向之间间隔多少行
	 * @param XCount 横向共有水印多少个
	 * @param YCount 纵向共有水印多少个
	 * @param waterRemarkWidth 水印图片宽度为多少列
	 * @param waterRemarkHeight 水印图片高度为多少行
	 * @throws IOException
	 */
	public static void putWaterRemarkToExcel(Workbook wb, Sheet sheet, BufferedImage bufferImg, int startXCol,
			int startYRow, int betweenXCol, int betweenYRow, int XCount, int YCount, int waterRemarkWidth,
			int waterRemarkHeight) throws IOException {

		// 导出到字节流B
		ByteArrayOutputStream os = new ByteArrayOutputStream();
		ImageIO.write(bufferImg, "png", os);
		// 加载图片
		// 开始打水印
		Drawing drawing = sheet.createDrawingPatriarch();

		// 按照共需打印多少行水印进行循环
		for (int yCount = 0; yCount < YCount; yCount++) {
			// 按照每行需要打印多少个水印进行循环
			for (int xCount = 0; xCount < XCount; xCount++) {
				// 创建水印图片位置
				int xIndexInteger = startXCol + (xCount * waterRemarkWidth) + (xCount * betweenXCol);
				int yIndexInteger = startYRow + (yCount * waterRemarkHeight) + (yCount * betweenYRow);
				/*
				 * 参数定义： 第一个参数是（x轴的开始节点）； 第二个参数是（是y轴的开始节点）； 第三个参数是（是x轴的结束节点）；
				 * 第四个参数是（是y轴的结束节点）； 第五个参数是（是从Excel的第几列开始插入图片，从0开始计数）；
				 * 第六个参数是（是从excel的第几行开始插入图片，从0开始计数）； 第七个参数是（图片宽度，共多少列）； 第8个参数是（图片高度，共多少行）；
				 */
				ClientAnchor anchor = drawing.createAnchor(0, 0, 0, 0, xIndexInteger, yIndexInteger,
						xIndexInteger + waterRemarkWidth, yIndexInteger + waterRemarkHeight);

				Picture pic = drawing.createPicture(anchor, wb.addPicture(os.toByteArray(), Workbook.PICTURE_TYPE_PNG));
				pic.resize();
			}
		}
	}

	/**
	 * excel设置水印
	 * @param file
	 * @param markStr
	 */
	public static void setExcelWaterMark(File file, String markStr, String type) {
		try {
			FontImage.Watermark watermark = new FontImage.Watermark();
			watermark.setText("水印测试");
			BufferedImage image = FontImage.createWatermarkImage(null);
			if (type.equals("xlsx")) {
				XSSFWorkbook workbook = new XSSFWorkbook(new FileInputStream(file));

				// 获取excel sheet个数
				int sheets = workbook.getNumberOfSheets();
				// 循环sheet给每个sheet添加水印
				for (int i = 0; i < sheets; i++) {
					XSSFSheet sheet = workbook.getSheetAt(i);
					// 导出到字节流B
					ByteArrayOutputStream os = new ByteArrayOutputStream();
					ImageIO.write(image, "png", os);

					int pictureIdx = workbook.addPicture(os.toByteArray(), Workbook.PICTURE_TYPE_PNG);
					// add relation from sheet to the picture data
					String rID = sheet.addRelation(null, XSSFRelation.IMAGES, workbook.getAllPictures().get(pictureIdx))
							.getRelationship().getId();
					// set background picture to sheet
					sheet.getCTWorksheet().addNewPicture().setId(rID);
				}

				// ByteArrayOutputStream bos = new ByteArrayOutputStream();
				// workbook.write(bos);
				// byte[] barray = bos.toByteArray();
				// InputStream is = new ByteArrayInputStream(barray);
				// MultipartFile multipartFile = new MockMultipartFile(file.getName(),
				// file.getOriginalFilename(), "", is);
				// 保存位置
				String downloadPath = "I:/Desktop/图片/word/" + System.currentTimeMillis() + "_"
						+ StrUtil.subAfter("I:/Desktop/图片/word/1111.XLSX", "/", true);

				// 生成输出文件
				File file11 = new File(downloadPath);
				file11.createNewFile();
				workbook.write(new FileOutputStream(file11));
				workbook.close();

			}
			else {
				HSSFWorkbook wb = new HSSFWorkbook(new FileInputStream(file));
				// 获取excel sheet个数
				int sheets = wb.getNumberOfSheets();
				// 循环sheet给每个sheet添加水印
				for (int i = 0; i < sheets; i++) {
					Sheet sheet = wb.getSheetAt(i);
					putWaterRemarkToExcel(wb, sheet, image, 0, 0, 6, 10, 10, 10, 0, 0);
				}
				// ByteArrayOutputStream bos = new ByteArrayOutputStream();
				// wb.write(bos);
				// byte[] barray = bos.toByteArray();
				// InputStream is = new ByteArrayInputStream(barray);
				// MultipartFile multipartFile = new MockMultipartFile(file.getName(),
				// file.getOriginalFilename(), "", is);
				//
				// 保存位置
				String downloadPath = "I:/Desktop/图片/word/" + System.currentTimeMillis() + "_"
						+ StrUtil.subAfter("I:/Desktop/图片/word/1111.XLSX", "/", true);

				// 生成输出文件
				File file11 = new File(downloadPath);
				file11.createNewFile();
				wb.write(new FileOutputStream(file11));
				wb.close();
			}

		}
		catch (Exception e) {
			e.printStackTrace();
		}
	}

	/**********************************
	 * ---------------------PDF添加水印----------------------
	 **********************************/

	/**
	 * @param file 原PDF文件
	 * @param waterMarkName 页脚添加水印
	 * @throws DocumentException
	 * @throws IOException
	 */
	public static MultipartFile setPDFWatermark(MultipartFile file, String waterMarkName) {
		try {

			ByteArrayOutputStream bos = new ByteArrayOutputStream();
			PdfReader reader = new PdfReader(file.getInputStream());
			PdfStamper stamper = new PdfStamper(reader, bos);

			// 获取总页数 +1, 下面从1开始遍历
			int total = reader.getNumberOfPages() + 1;
			// 使用classpath下面的字体库
			BaseFont base = null;
			try {
				base = BaseFont.createFont("STSong-Light", "UniGB-UCS2-H", BaseFont.EMBEDDED);
			}
			catch (Exception e) {
				// 日志处理
				e.printStackTrace();
			}

			// 间隔
			int interval = -5;
			// 获取水印文字的高度和宽度
			int textH = 0, textW = 0;
			JLabel label = new JLabel();
			label.setText(waterMarkName);
			FontMetrics metrics = label.getFontMetrics(label.getFont());
			textH = metrics.getHeight();
			textW = metrics.stringWidth(label.getText());
			System.out.println("textH: " + textH);
			System.out.println("textW: " + textW);

			// 设置水印透明度
			PdfGState gs = new PdfGState();
			gs.setFillOpacity(0.2f);
			gs.setStrokeOpacity(0.2f);

			com.itextpdf.text.Rectangle pageSizeWithRotation = null;
			PdfContentByte content = null;
			for (int i = 1; i < total; i++) {
				// 在内容上方加水印
				content = stamper.getOverContent(i);
				// 在内容下方加水印
				// content = stamper.getUnderContent(i);
				content.saveState();
				content.setGState(gs);

				// 设置字体和字体大小
				content.beginText();
				content.setFontAndSize(base, 20);

				// 获取每一页的高度、宽度
				pageSizeWithRotation = reader.getPageSizeWithRotation(i);
				float pageHeight = pageSizeWithRotation.getHeight();
				float pageWidth = pageSizeWithRotation.getWidth();

				// 根据纸张大小多次添加， 水印文字成30度角倾斜
				for (int height = interval + textH; height < pageHeight; height = height + textH * 3) {
					for (int width = interval + textW; width < pageWidth + textW; width = width + textW * 2) {
						content.showTextAligned(Element.ALIGN_LEFT, waterMarkName, width - textW, height - textH, 30);
					}
				}

				content.endText();
			}

			// 关流
			stamper.close();
			reader.close();
			bos.close();
			InputStream is = new ByteArrayInputStream(bos.toByteArray());
			MultipartFile multipartFile = new MockMultipartFile(file.getName(), file.getOriginalFilename(), "", is);
			return multipartFile;
		}
		catch (DocumentException e) {
			e.printStackTrace();
		}
		catch (IOException e) {
			e.printStackTrace();
		}
		return null;
	}

}
